package models

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"getshop/models/db_struct"
	redisAdmin "getshop/redis/admin"
	"github.com/astaxie/beego/orm"
	"net/url"
	"time"
)

//AdminAccessToken struct
type AdminAccessToken struct {
	App
}

//AdminAccessTokenData struct
type AdminAccessTokenData struct {
	Aid        int
	AuthTarget int8
	ExpiryTime int64
}

//获取公钥
func (m *AdminAccessToken) GetOpenSSLPublicKey() string {
	key := "-----BEGIN PUBLIC KEY-----" + "\n"
	key += "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDiO05weuTEd9nq8iAXDKpGCNeY" + "\n"
	key += "ELS1LTifQtbkzKZxllcxrXRmehfh+NwaECCM/pqs7CKXBVlnmyGRNz27SjUbBE37" + "\n"
	key += "M8fRsUHyKvyczoTWLO+Eh51wDl2QoeJngeej8z53sRkW+raKtS9bs8kwLNriEYxa" + "\n"
	key += "gFhM2sstI4SH+dQ7xwIDAQAB" + "\n"
	key += "-----END PUBLIC KEY-----"
	return key
}

//获取私钥
func (m *AdminAccessToken) GetOpenSSLPrivateKey() string {
	key := "-----BEGIN RSA PRIVATE KEY-----" + "\n"
	key += "MIICXAIBAAKBgQDiO05weuTEd9nq8iAXDKpGCNeYELS1LTifQtbkzKZxllcxrXRm" + "\n"
	key += "ehfh+NwaECCM/pqs7CKXBVlnmyGRNz27SjUbBE37M8fRsUHyKvyczoTWLO+Eh51w" + "\n"
	key += "Dl2QoeJngeej8z53sRkW+raKtS9bs8kwLNriEYxagFhM2sstI4SH+dQ7xwIDAQAB" + "\n"
	key += "AoGAUpBuaiKPxxwujc5b482dfv3PIRJHwS+F0KnmH46I2D+UBu9U4emBOdlHCsQD" + "\n"
	key += "/TEWZjGv1gn/vQ4izAqrHuVQR6Ixqajl3eC95HribXFa7RGb3YsqeldBdTt1G3z/" + "\n"
	key += "Az9n9AlfcFPujhMyDcfeFFpFWqQkhwCietRLq9Z1j8OkuOECQQD1zI83aQxZpGRt" + "\n"
	key += "WpSDRWUqEgUfIkHe7B/iW9+MpRyznfwFwtvupYNS4DTmxvp/xVyGs4N24qxiF2A5" + "\n"
	key += "Y8WvKpU/AkEA657bdvhan91f7C6rXuqaYll4vVuMA0MnhzkNCn1fQbu31CSoDj2T" + "\n"
	key += "XRWqLFVJLHSp5VzMcyCbJ4YNqvFG6csPeQJAMWbv0f83ED1OQfxWpRupfY8poFxO" + "\n"
	key += "SPPEZMZ6Y9Y7so4qPS4w7gIuJtYRS+DqMAwG0Lv7zEU6yGxSVraK4O36GQJAcqfL" + "\n"
	key += "3dm+p7pe2hxfsgFxfNhTA7oPqOq9ynR9nxQhDWiebLIlwUA/fHV1aLtbrzFaF02Q" + "\n"
	key += "A5njRllaP0vHmS8B6QJBANM806D0js3+I50Hti1Z9q/omzQEpHqP8Jys1tHLnxQv" + "\n"
	key += "x5bABLQZ8yfY9QD4Kiga/kAQWRoFLpBvRz5zsICZ2gE=" + "\n"
	key += "-----END RSA PRIVATE KEY-----"
	return key
}

//生成用户Token
func (m *AdminAccessToken) GenerateToken(aid int, authTarget int8, expiryTime int64) (error, string) {
	o := orm.NewOrm()
	if !o.QueryTable("Admin").Filter("Id", aid).Exist() {
		return errors.New("用户不存在！"), ""
	}
	if expiryTime <= 0 {
		//默认1天后过期
		expiryTime = time.Now().Unix() + 86400
	}
	//生成Token
	token := AdminAccessTokenData{
		Aid:        aid,
		AuthTarget: authTarget,
		ExpiryTime: expiryTime,
	}
	tokenTypes, err := json.Marshal(token)
	if err != nil {
		return err, ""
	}
	block, _ := pem.Decode([]byte(m.GetOpenSSLPublicKey()))
	if block == nil {
		return errors.New("服务器内部错误无法创建Token"), ""
	}
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err, ""
	}
	encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, pub.(*rsa.PublicKey), tokenTypes)
	if err != nil {
		return err, ""
	}
	accessToken := url.QueryEscape(base64.StdEncoding.EncodeToString(encryptedData))
	//保存数据
	adminAccessToken := &db_struct.AdminAccessToken{}
	err = o.QueryTable("AdminAccessToken").Filter("Aid", aid).Filter("AuthTarget", authTarget).One(adminAccessToken, "Id")
	switch err {
	case nil:
		adminAccessToken.Token = accessToken
		adminAccessToken.ExpiryTime = expiryTime
		_, err := o.Update(adminAccessToken, "Token", "ExpiryTime")
		if err != nil {
			return err, ""
		}
	case orm.ErrNoRows:
		adminAccessToken = &db_struct.AdminAccessToken{
			Aid:        aid,
			AuthTarget: authTarget,
			Token:      accessToken,
			ExpiryTime: expiryTime,
		}
		_, err := o.Insert(adminAccessToken)
		if err != nil {
			return err, ""
		}
	default:
		return err, ""
	}
	return nil, accessToken
}

//根据Token获取用户Id
func (m *AdminAccessToken) GetAdminIdByToken(token string) (error, int) {
	if token == "" {
		return errors.New("错误：Token不能为空"), 0
	}
	str, _ := url.QueryUnescape(token)
	accessTokenBytes, err := base64.StdEncoding.DecodeString(str)
	if err != nil {
		return err, 0
	}
	//解密
	block, _ := pem.Decode([]byte(m.GetOpenSSLPrivateKey()))
	if block == nil {
		return errors.New("服务器内部错误无法解析token"), 0
	}
	private, err := x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return errors.New("解析token出错"), 0
	}
	tokenJsonBytes, err := rsa.DecryptPKCS1v15(rand.Reader, private, accessTokenBytes)
	if err != nil {
		return errors.New("解析token出错"), 0
	}
	td := &AdminAccessTokenData{}
	err = json.Unmarshal(tokenJsonBytes, td)
	if err != nil {
		return errors.New("解析token出错：" + err.Error()), 0
	}
	if td.ExpiryTime < time.Now().Unix() {
		return errors.New("token已经过期"), 0
	}
	if td.Aid == 0 {
		return errors.New("token错误"), 0
	}
	return nil, td.Aid
}

//验证管理员Token
func (m *AdminAccessToken) ValidateToken(aid int, authTarget int8, accessToken string) error {
	rdsAdmin := redisAdmin.NewAdmin()
	rdsAdmin.Read()
	if rdsAdmin.Token[authTarget] == "" {
		o := orm.NewOrm()
		adminAccessToken := &db_struct.AdminAccessToken{}
		err := o.QueryTable("UserAccessToken").Filter("Aid", aid).Filter("AuthTarget", authTarget).One(adminAccessToken, "Token")
		switch err {
		case nil:
		case orm.ErrNoRows:
			return errors.New("请先登录")
		default:
			return err
		}
		rdsAdmin.Token[authTarget] = adminAccessToken.Token
	}
	rdsAdmin.Save()
	if accessToken != rdsAdmin.Token[authTarget] {
		return errors.New("登录过期Token错误")
	}
	return nil
}

//NewAdminAccessToken
func NewAdminAccessToken(ormer orm.Ormer) *AdminAccessToken {
	o := &AdminAccessToken{}
	o.Ormer = ormer
	o.FlagFiled = map[string]map[int8]string{
		"AuthTarget": {
			1: "WebApp",
			2: "MobileApp",
		},
	}
	return o
}
